using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Collections;

using SlimDX;
using SlimDX.Direct3D9;

using miciv;
using miciv.Rendering;
using micivCore.Components;
using micivCore.Data;
using micivCore.DataSource;
using miciv.Misc;
using miciv.Job;
using micivCore.Job;
using micivCore.Player;

namespace micivCore.Level
{
    public interface ILevelAccessor
    {
        Level Level { get; set; }
    }

    public class Level
    {
        protected struct PostInitializeData
        {
            public Entity m_entity;
            public XmlElement m_element;

            public PostInitializeData(Entity _entity, XmlElement _element)
            {
                this.m_entity = _entity;
                this.m_element = _element;
            }
        }

        protected CoreContext m_coreContext;
        public CoreContext CoreContext
        {
            get
            {
                return this.m_coreContext;
            }
        }

        protected string m_strID;
        public string ID
        {
            get
            {
                return this.m_strID;
            }
        }

        protected IMapCommunicator m_mapCommunicator;
        public IMapCommunicator MapCommunicator
        {
            get
            {
                return this.m_mapCommunicator;
            }
            set
            {
                this.m_mapCommunicator = value;
            }
        }

        protected List<Entity> m_lstEntities;
        protected List<IPlayer> m_lstPlayer;
        protected IPlayer m_currentPlayer;

        public Level(CoreContext _coreContext)
        {
            this.m_coreContext = _coreContext;
            this.m_lstEntities = new List<Entity>();
            this.m_lstPlayer = new List<IPlayer>();
            this.m_currentPlayer = null;
        }

        public bool Initialize(XmlElement _xmlLevel, List<PlayerDescription> _lstPlayerDescs)
        {
            this.m_strID = _xmlLevel.Attributes["id"].Value;
            foreach (PlayerDescription desc in _lstPlayerDescs)
            {
                switch (desc.PlayerType)
                {
                    case PlayerType.CPU:
                    {
                        this.m_lstPlayer.Add(new CPUPlayer(this.CoreContext, this, desc));
                        break;
                    }
                    case PlayerType.LOCAL:
                    {
                        this.m_lstPlayer.Add(new LocalPlayer(this.CoreContext, this, desc));
                        break;
                    }
                    case PlayerType.REMOTE:
                    {
                        this.m_lstPlayer.Add(new RemotePlayer(this.CoreContext, this, desc));
                        break;
                    }
                }
            }

            XmlElement xmlEntities = _xmlLevel.SelectSingleNode("entities") as XmlElement;
            if (null != xmlEntities)
            {
                this.InitializeEntities(xmlEntities);
            }

            XmlElement xmlSpawnlocations = _xmlLevel.SelectSingleNode("spawnlocations") as XmlElement;
            if (null != xmlSpawnlocations)
            {
                this.InitializeSpawnLocation(xmlSpawnlocations);
            }
            return true;
        }

        protected void InitializeEntities(XmlElement _xmlEntities)
        {
            CoreManager coreManager = this.m_coreContext.CoreManager;
            XmlNodeList lstXmlEntities = _xmlEntities.SelectNodes("entity");
            List<PostInitializeData> lstEntityData = new List<PostInitializeData>(lstXmlEntities.Count);
            foreach (XmlElement xmlEntity in lstXmlEntities)
            {
                EntityPrototype enProto = coreManager.GetEntityPrototype(xmlEntity.Attributes["prototypeid"].Value);
                LevelEntityBase entity = coreManager.CreateEntity<LevelEntityBase>(enProto);
                if (null == entity)
                {
                    throw new Exception(string.Format("{1} : could not create {0} entity.", xmlEntity.Attributes["prototypeid"].Value, _xmlEntities.OwnerDocument.BaseURI));
                }
                entity.Level = this;
                this.m_lstEntities.Add(entity);
                lstEntityData.Add(new PostInitializeData(entity, xmlEntity));
            }
            foreach (PostInitializeData piData in lstEntityData)
            {
                piData.m_entity.PostInitialize(piData.m_element);
            }
        }

        protected void InitializeSpawnLocation(XmlElement _xmlSpawnlocations)
        {
        }

        public void Update(float _deltaTime)
        {
            if (null == this.m_currentPlayer)
            {
                SetCurrentPlayer(this.m_lstPlayer[0]);
            }
            if (null != this.m_currentPlayer)
            {
                this.m_currentPlayer.Update();
            }

            foreach (Entity entity in this.m_lstEntities)
            {
                entity.Update(_deltaTime);
            }
        }

        public void Release()
        {
            CoreManager coreManager = this.m_coreContext.CoreManager;
            foreach (Entity entity in this.m_lstEntities)
            {
                coreManager.ReleaseEntity(entity);
            }
            this.m_lstEntities.Clear();

            this.m_lstPlayer[0].Deactivate();
            foreach (IPlayer player in this.m_lstPlayer)
            {
                player.Release();
            }
            this.m_lstPlayer.Clear();
        }

        protected void SetCurrentPlayer(IPlayer _player)
        {
            if (_player != this.m_currentPlayer)
            {
                LuaManager luaManager = this.CoreContext.GetData<LuaManager>("LuaManager");
                if (null != this.m_currentPlayer)
                {
                    this.m_currentPlayer.Deactivate();
                    luaManager.State["Player"] = null;
                }
                this.m_currentPlayer = _player;
                if (null != this.m_currentPlayer)
                {
                    this.m_currentPlayer.Activate();
                    luaManager.State["Player"] = this.m_currentPlayer;
                }
            }
        }

        public IPlayer GetPlayer(int index)
        {
            if (index < this.m_lstPlayer.Count)
            {
                return this.m_lstPlayer[index];
            }
            return null;
        }
    }
}
